[None][fix] Fix one-model MTP KV cache accounting - #17264
Conversation
|
/bot run |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughThe change updates speculative decoding across executor scheduling and FlashInfer KV-cache handling. It preserves Python draft tokens, applies and restores backend KV-length offsets, separates logical lengths from reserved page-table capacity, and adds regression coverage. ChangesSpeculative decoding and KV-cache updates
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Scheduler
participant ModelEngine
participant FlashInfer
participant DecodeWrapper
Scheduler->>ModelEngine: build speculative generation metadata
ModelEngine->>FlashInfer: apply KV-length offsets
FlashInfer->>DecodeWrapper: publish logical KV lengths
ModelEngine->>FlashInfer: prepare and plan decode
ModelEngine->>FlashInfer: restore KV-length offsets
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1⚔️ Resolve merge conflicts 💡
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/unittest/_torch/executor/test_py_executor.py`:
- Around line 2010-2013: Update the draft-token assertions in the affected
executor test to verify values, not only lengths: compare both
gen.py_draft_tokens and disagg_gen.py_draft_tokens against [0] *
self.MAX_TOTAL_DRAFT_TOKENS. Keep the existing num_draft_tokens assertions
unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: cfdc40e2-408d-41fd-8732-7561ca155193
📒 Files selected for processing (4)
tensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor.pytests/unittest/_torch/executor/test_py_executor.pytests/unittest/_torch/executor/test_pytorch_model_engine.py
|
PR_Github #63826 [ run ] triggered by Bot. Commit: |
|
PR_Github #63826 [ run ] completed with state
|
|
Could you clarify how these two hunks relate?
The |
0baecbe to
990299f
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/unittest/_torch/executor/test_py_executor.py`:
- Around line 2036-2037: Update the sampler draft setup in the relevant executor
test to use a nonempty list shorter than self.MAX_TOTAL_DRAFT_TOKENS, while
retaining the assertion that request.draft_tokens uses the full scheduler budget
and request.py_draft_tokens preserves the supplied shorter list.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 7989f080-973b-451a-9155-656f54711e18
📒 Files selected for processing (4)
tensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor.pytests/unittest/_torch/executor/test_py_executor.pytests/unittest/_torch/executor/test_pytorch_model_engine.py
🚧 Files skipped from review as they are similar to previous changes (3)
- tests/unittest/_torch/executor/test_pytorch_model_engine.py
- tensorrt_llm/_torch/pyexecutor/py_executor.py
- tensorrt_llm/_torch/pyexecutor/model_engine.py
|
These changes address two parts of the same MTP + FlashInfer + block-reuse failure path. The python and C++ draft-token representations must both be populated because they have different consumers: the C++ micro-batch scheduler uses The first-generation I’m keeping them together because both are needed for this MTP + FlashInfer + block-reuse fix. |
990299f to
9ff8de8
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
/bot run |
|
PR_Github #64088 [ run ] triggered by Bot. Commit: |
|
PR_Github #64088 [ run ] completed with state
|
|
/bot run |
|
PR_Github #64146 [ run ] triggered by Bot. Commit: |
|
PR_Github #64146 [ run ] completed with state
|
|
/bot run |
|
PR_Github #64208 [ run ] triggered by Bot. Commit: |
|
PR_Github #64208 [ run ] completed with state
|
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
tensorrt_llm/_torch/pyexecutor/model_engine.py (1)
2968-2992: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winApply FlashInfer offsets to extend-context rows.
FlashInferAttentionMetadata.apply_spec_decode_kv_lens_offsets()returns whennum_generations == 0. In extend-context mode, extend requests are included innum_contexts, so their rows are[num_contexts - num_chunked_ctx_requests:num_contexts]and are not corrected. Add equivalent chunked-context handling in both preprocessing and restoration.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/pyexecutor/model_engine.py` around lines 2968 - 2992, Update both preprocessing and restoration flows around apply_spec_decode_kv_lens_offsets so FlashInfer metadata also adjusts chunked extend-context rows. When num_chunked_ctx_requests > 0, apply previous_kv_lens_offsets_cuda to rows [num_ctx_requests - num_chunked_ctx_requests:num_ctx_requests], including cases with zero generations; preserve the existing generation-row handling for other requests.
🧹 Nitpick comments (2)
tensorrt_llm/_torch/pyexecutor/model_engine.py (2)
3485-3492: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
use_full_generation_page_tablegating condition.This exact four-clause expression is repeated verbatim at Line 5098-5101 in
_prepare_tp_inputs. Both sites gate the same correctness-critical feature (whether generation rows get the full reservation-width page table). If one site's condition changes in the future (e.g. a new gating factor) without the other, the two code paths silently diverge on this KV-cache-accounting decision. Extract a small helper, e.g.self._should_use_full_generation_page_table(spec_config, attn_metadata).♻️ Proposed refactor
+ def _should_use_full_generation_page_table( + self, spec_config: Optional[DecodingBaseConfig], + attn_metadata: AttentionMetadata) -> bool: + return (self.enable_spec_decode and not self._disable_overlap_scheduler + and getattr(spec_config, '_use_shared_kv_cache', False) + and hasattr(attn_metadata, 'apply_spec_decode_kv_lens_offsets')) + def _prepare_incremental_update_metadata( self, ... attn_metadata.kv_cache_params = KVCacheParams( use_cache=True, num_cached_tokens_per_seq=num_cached_tokens_per_seq, num_extra_kv_tokens=get_num_extra_kv_tokens(spec_config), - use_full_generation_page_table=( - self.enable_spec_decode and not self._disable_overlap_scheduler - and getattr(spec_config, '_use_shared_kv_cache', False) and - hasattr(attn_metadata, 'apply_spec_decode_kv_lens_offsets'))) + use_full_generation_page_table=self._should_use_full_generation_page_table( + spec_config, attn_metadata))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/pyexecutor/model_engine.py` around lines 3485 - 3492, Extract the repeated four-clause `use_full_generation_page_table` condition from the KVCacheParams construction and `_prepare_tp_inputs` into a shared helper such as `_should_use_full_generation_page_table(spec_config, attn_metadata)`. Replace both inline expressions with calls to this helper while preserving the existing gating behavior.
5094-5101: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicated
use_full_generation_page_tablegating condition (second site).Same expression as Line 3489-3492 in
_prepare_incremental_update_metadata. See the comment on that range; both should call one shared helper.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/pyexecutor/model_engine.py` around lines 5094 - 5101, Extract the repeated use_full_generation_page_table eligibility expression into a shared helper, then update both _prepare_incremental_update_metadata and this KVCacheParams construction to call it. Preserve the existing checks for spec decoding, overlap scheduling, shared KV cache, and apply_spec_decode_kv_lens_offsets.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tensorrt_llm/_torch/pyexecutor/py_executor.py`:
- Around line 3313-3337: Pass the resolved maximum sequence length into the
AutoDeploy PyExecutor constructor so self.max_seq_len is initialized, using the
engine cache sequence interface’s max_seq_len value. Ensure
_one_model_mtp_batch_needs_zero_draft can compare max_target_position against a
valid limit during one-model MTP drafting.
---
Outside diff comments:
In `@tensorrt_llm/_torch/pyexecutor/model_engine.py`:
- Around line 2968-2992: Update both preprocessing and restoration flows around
apply_spec_decode_kv_lens_offsets so FlashInfer metadata also adjusts chunked
extend-context rows. When num_chunked_ctx_requests > 0, apply
previous_kv_lens_offsets_cuda to rows [num_ctx_requests -
num_chunked_ctx_requests:num_ctx_requests], including cases with zero
generations; preserve the existing generation-row handling for other requests.
---
Nitpick comments:
In `@tensorrt_llm/_torch/pyexecutor/model_engine.py`:
- Around line 3485-3492: Extract the repeated four-clause
`use_full_generation_page_table` condition from the KVCacheParams construction
and `_prepare_tp_inputs` into a shared helper such as
`_should_use_full_generation_page_table(spec_config, attn_metadata)`. Replace
both inline expressions with calls to this helper while preserving the existing
gating behavior.
- Around line 5094-5101: Extract the repeated use_full_generation_page_table
eligibility expression into a shared helper, then update both
_prepare_incremental_update_metadata and this KVCacheParams construction to call
it. Preserve the existing checks for spec decoding, overlap scheduling, shared
KV cache, and apply_spec_decode_kv_lens_offsets.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: c80bbdf5-195b-4a3d-93f2-146f12daecfb
📒 Files selected for processing (8)
tensorrt_llm/_torch/attention_backend/flashinfer.pytensorrt_llm/_torch/metadata.pytensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytests/unittest/_torch/attention/test_flashinfer_attention.pytests/unittest/_torch/executor/test_py_executor.pytests/unittest/_torch/executor/test_pytorch_model_engine.py
275e929 to
55330f2
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
tensorrt_llm/_torch/attention_backend/flashinfer.py (2)
23-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse built-in generic annotations.
Replace the new
List[int]annotations withlist[int]. RemoveListfrom the import only if no remaining annotation needs it.As per coding guidelines, “Use Python 3.10+” and “prefer built-in generic types.”
Also applies to: 99-103
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/attention_backend/flashinfer.py` around lines 23 - 24, Update the annotations in flashinfer.py, including the additional locations around lines 99–103, to use the built-in list[int] syntax instead of List[int]. Remove List from the typing import only if no other annotation in the file still references it.Source: Coding guidelines
801-808: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDocument the offset contract explicitly.
Add Google-style
Args:documentation. State the ordering and sign ofoffsets, thetokens_per_generationtoken mapping, and the invariant thatrestore=Trueexactly reverses the mutation.PyTorchModelEnginedepends on this contract for both generation and chunked-context rows.As per coding guidelines, use Google-style docstrings for functions. Based on PR objectives, this offset invariant spans Python scheduling and FlashInfer runtime state.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tensorrt_llm/_torch/attention_backend/flashinfer.py` around lines 801 - 808, The docstring for the FlashInfer runtime-state rewind function must explicitly document its offset contract. Add a Google-style Args section describing offset ordering and sign, how tokens_per_generation maps tokens, and that restore=True exactly reverses the mutation; keep the contract applicable to both generation and chunked-context rows used by PyTorchModelEngine.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tensorrt_llm/_torch/attention_backend/flashinfer.py`:
- Around line 1519-1537: Update _do_plan_mla_decode() so kv_len_arr uses logical
KV lengths from _logical_kv_lens when _uses_full_generation_page_table is
enabled, rather than reservation-width paged_kv_indptr_decode values. Apply
overlap offsets to the logical lengths before MLA planning, while preserving
existing behavior for non-reserved page tables. Add focused MLA tests covering
both a reserved page and a nonzero overlap offset.
---
Nitpick comments:
In `@tensorrt_llm/_torch/attention_backend/flashinfer.py`:
- Around line 23-24: Update the annotations in flashinfer.py, including the
additional locations around lines 99–103, to use the built-in list[int] syntax
instead of List[int]. Remove List from the typing import only if no other
annotation in the file still references it.
- Around line 801-808: The docstring for the FlashInfer runtime-state rewind
function must explicitly document its offset contract. Add a Google-style Args
section describing offset ordering and sign, how tokens_per_generation maps
tokens, and that restore=True exactly reverses the mutation; keep the contract
applicable to both generation and chunked-context rows used by
PyTorchModelEngine.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 67cd2d73-3769-4cf3-802d-c37ce54b73d9
📒 Files selected for processing (8)
tensorrt_llm/_torch/attention_backend/flashinfer.pytensorrt_llm/_torch/pyexecutor/kv_cache_manager_v2.pytensorrt_llm/_torch/pyexecutor/model_engine.pytensorrt_llm/_torch/pyexecutor/py_executor.pytensorrt_llm/_torch/pyexecutor/resource_manager.pytests/unittest/_torch/attention/test_flashinfer_attention.pytests/unittest/_torch/executor/test_py_executor.pytests/unittest/_torch/executor/test_pytorch_model_engine.py
🚧 Files skipped from review as they are similar to previous changes (5)
- tensorrt_llm/_torch/pyexecutor/resource_manager.py
- tensorrt_llm/_torch/pyexecutor/py_executor.py
- tests/unittest/_torch/executor/test_pytorch_model_engine.py
- tests/unittest/_torch/executor/test_py_executor.py
- tests/unittest/_torch/attention/test_flashinfer_attention.py
d308f32 to
c318de4
Compare
|
/bot run |
|
PR_Github #65147 [ run ] triggered by Bot. Commit: |
|
PR_Github #65147 [ run ] completed with state
|
|
/bot run |
|
PR_Github #65201 [ run ] triggered by Bot. Commit: |
|
PR_Github #65201 [ run ] completed with state
|
chienchunhung
left a comment
There was a problem hiding this comment.
LGTM. I left one non-blocking comment about AutoDeploy path.
c318de4 to
3777d74
Compare
3777d74 to
abbd34f
Compare
| if spec_config is not None and spec_config.is_linear_tree else | ||
| self.model_engine.max_total_draft_tokens) | ||
|
|
||
| if self._one_model_mtp_batch_needs_zero_draft(scheduled_batch): |
There was a problem hiding this comment.
Can we make this boundary decision group-uniform under attention DP? Each rank schedules different requests, so one rank can set runtime_draft_len=0 and return through skip_drafting() while a peer keeps K and enters DeepseekV3MTPHead's LM-head-TP all-gather. That leaves an unmatched collective and hangs the group. Please OR the unsafe flag across the TP/ADP group before changing the batch-wide draft length, and add a two-rank near-limit test (optional).
There was a problem hiding this comment.
Good catch!
_handle_dynamic_draft_len() now computes a local runtime_draft_len first, ORs the unsafe flag across the TP/ADP group, and only then mutates runtime_draft_len and request state. All three call sites are already inside if can_queue, which is itself a TP allgather under ADP, so every rank enters the new collective exactly once in the same position; the non-ADP path stays collective-free.
Note that as a result, the diff is quite large: the speculation_permanently_disabled is an early return, and the pad / truncate loop moved to the tail behind if not dynamic_draft_len_enabled: return, and _one_model_mtp_batch_needs_zero_draft() now takes runtime_draft_len as a parameter since it can no longer read the not-yet-committed engine field. Note one deliberate semantic flip inside it: runtime_draft_len == 0 now returns True instead of False, so a locally selected zero also participates in the OR.
One more thing worth pointing at in the diff, since it is easy to read as unrelated: the clear is now if needs_zero_draft or runtime_draft_len == 0.
My first pass at this restructure folded the old unconditional speculation_permanently_disabled clear into the needs_zero_draft block, which is gated on is_mtp_eagle_one_model(). That silently dropped the clear for vanilla MTP and Eagle3 one-model, leaving K python draft tokens alongside runtime_draft_len == 0. Clearing on any committed zero restores it; test_permanent_disable_clears_non_mtp_eagle_draft_tokens is the corresponding test.
| # disable drafting for every generation request in this batch. | ||
| for request in scheduled_batch.generation_requests: | ||
| request.py_draft_tokens = [] | ||
| self.model_engine.runtime_draft_len = 0 |
There was a problem hiding this comment.
This zero-draft decision is lost for dynamic-tree MTP: overlap still supplies next_draft_tokens_device, so _prepare_tp_inputs() classifies the request as extend and then unconditionally resets runtime_draft_len to max_total_draft_tokens for non-linear trees. A near-limit max_draft_len=3, topK=4 request therefore executes all 12 tree tokens after this code selected zero. Can we preserve the explicit zero through TP-input preparation and extend the regression through that stage?
There was a problem hiding this comment.
Agreed.
_prepare_tp_inputs() no longer rewrites runtime_draft_len unconditionally for non-linear trees; the normalization is now skipped when the executor already selected zero.
This also explains the comment-only change at can_promote_spec_decode: its old justification no longer holds.
Regression extended past _handle_dynamic_draft_len() and through _prepare_tp_inputs() with a non-None next_draft_tokens_device, and is now tested in test_dynamic_tree_prepare_preserves_explicit_zero_draft.
| # reads back in `_prepare_tp_inputs` - and would also suppress the | ||
| # `current_num_draft_tokens == 0` signal that `_handle_dynamic_draft_len` uses to | ||
| # request a one-hot draft-probs placeholder under rejection sampling. | ||
| if not request.py_draft_tokens: |
There was a problem hiding this comment.
Can we preserve the empty Python-side draft signal until _handle_dynamic_draft_len() consumes it? After a K=0 iteration, SpecSampler writes py_draft_tokens=[]; filling it here before the handler makes py_needs_onehot_draft_probs stay false. Since that slot produced no proposal row at K=0, the next rejection step gathers stale draft_probs and biases sampling. The scheduler only needs request.draft_tokens, so the Python placeholder can be deferred; please add a rejection-enabled K->0->K regression.
There was a problem hiding this comment.
Good catch!
Note that deferring the python placeholder would regress this PR's capacity fix, because get_draft_token_length() reads py_draft_tokens and SchedulerV2, KV allocation, and resource management all budget from it.
Instead, the zero-proposal fact is captured before the placeholder is inserte, and _prepare_tp_inputs() consumes it exactly once. Timing works out because a K=0 request has empty py_draft_tokens and therefore lands in generation_requests, not extend_requests. The flag isn't consumed until the resumed K>0 step, which is when the one-hot row is actually needed.
Covered by test_one_model_mtp_preserves_zero_proposal_signal_for_rejection.
| self._logical_kv_lens[row_slice].add_(runtime_offsets, | ||
| alpha=direction) | ||
|
|
||
| # Keep the host-preallocated page structure intact. In particular, `last_page_len` must stay |
There was a problem hiding this comment.
The page reservation can stay intact, but paged prefill still derives its causal KV length from this unchanged indptr/last-page pair (and cached-MLA context does likewise). With cached=31, q_len=4, and offset=-3, appends move to 28..31 while prefill still aligns Q to a 35-token KV, admitting future/stale slots. Can we publish the corrected logical context lengths to the prefill/MLA-context runtime, or reject this extend-context configuration? The added metadata-only SA test never reaches the real extend-context plan.
There was a problem hiding this comment.
Agreed, and I took the narrow option: overlap is now disabled for FlashInfer + any speculation mode that uses extend_ctx, rather than publishing corrected logical context lengths.
extend_ctx() returns not issubclass(backend, TrtllmAttention) unless use_one_engine(), so for FlashInfer the gate condition not use_one_engine() is exactly "this run would take the extend-context path" -> one-engine FlashInfer speculation keeps overlap.
A real device-side logical-length contract for paged prefill and cached MLA context (page-boundary rewinds, CUDA-graph-stable buffers, validated against an actual plan/run rather than metadata mutation) is materially larger, so I think it should be punted.
One qualification on severity, since it changes how you may want to weigh this: I could not find a public configuration that reaches the FlashInfer extend-context path today:
extend_ctx requires not use_one_engine(), and combined with support_overlap_scheduler(), that leaves exactly has_draft_model() - EAGLE3 two-model, MTP-Eagle two-model, and DraftTarget two-model. All three are currently unreachable. So I believe the stale-length bug you identified is real but currently latent, and this gate is a guard for retained legacy / internal states rather than a fix for a live path. The test exercises it by mutating that private flag and is labeled as such.
For diff-reading: this is why the inline overlap check in create_py_executor was extracted into _disable_unsupported_speculative_overlap_scheduler().
| # one iteration, so the sampler's first increment has not landed yet at this point | ||
| # and only here. This branch already assumes that same batch-to-batch continuity | ||
| # (previous_batch_idx indexes the immediately preceding batch's device tensors). | ||
| if (request.py_decoding_iter == 0 |
There was a problem hiding this comment.
This fixes only the first overlap generation for metadata without dynamic KV lengths. On every later step the host cached count is still the speculative upper bound, while the accepted-length correction is device-side; VanillaAttentionMetadata has neither kv_lens_cuda nor apply_spec_decode_kv_lens_offsets(). For P=5, width=4, and one accepted token, Vanilla uses cached=9 although the committed boundary is 6, even as positions rewind to 6. Can we implement the dynamic-length contract for these backends or reject overlap speculation for them?
There was a problem hiding this comment.
Another good catch!
Overlap speculation now disabled for the vanilla backend, and I removed the py_decoding_iter == 0 special case outright so nothing implies later steps are safe.
This is covered by the new TestUnsupportedSpeculativeOverlapScheduler, which asserts vanilla + speculation disables overlap, and that an already-disabled config is left alone.
| This is the entrypoint API to the _autodeploy backend. | ||
| """ | ||
| spec_config = ad_config.speculative_config | ||
| if spec_config is not None and spec_config.spec_dec_mode.is_mtp_eagle_one_model(): |
There was a problem hiding this comment.
Can we avoid testing spec_dec_mode here before AutoDeploy has resolved the checkpoint's MTP layer count? num_nextn_predict_layers is init=False; the input validator remaps it to max_draft_len, leaving it None, and this property treats None as one layer. AD also skips ModelLoader, the only pre-executor writer of that field. As a result every allowed AD MTP config—including the registered smoke/accuracy tests—hits this NotImplementedError. Please resolve the actual mode before this predicate or preserve the existing AD MTP path.
abbd34f to
973fd7d
Compare
* Why? Overlapped one-model MTP could overstate the cached length or expose too few FlashInfer pages during generation. This could access an invalid page and cause a CUDA illegal memory access. Draft-token accounting could also underreserve scheduler capacity or produce out-of-range positions near the sequence limit. * What? Keep Python and C++ draft-token state synchronized and disable drafting when the target positions could exceed the sequence limit. Use the prompt KV boundary for the first generation step when dynamic length correction is unavailable. For FlashInfer, expose all reserved generation pages while maintaining the logical KV lengths independently. Signed-off-by: William Zhang <133824995+2ez4bz@users.noreply.github.com>
* Why? One-model MTP can consume positions beyond the target verification span near the maximum sequence length. The shared-KV Gemma4 assistant could therefore index one rotary row past the model limit and poison the CUDA context. * What? Include shared and non-shared drafter lookahead in the zero-draft boundary check, with coverage for overlap and non-overlap scheduling. Signed-off-by: William Zhang <133824995+2ez4bz@users.noreply.github.com>
Signed-off-by: William Zhang <133824995+2ez4bz@users.noreply.github.com>
973fd7d to
65a41e8
Compare
Dev Engineer Review
KVCacheManagerandKVCacheManagerV2.BaseResourceManager.QA Engineer Review
tests/integration/test_lists/based on the available repository coverage entries.Description
The first overlapped MTP generation step could overstate its cached length and access an invalid FlashInfer page, causing a CUDA illegal memory access. The KV scheduler also lacked the Python-side draft-token count needed for correct capacity planning.
Keep the Python and C++ draft-token state synchronized, and use the prompt KV boundary when generation immediately follows context for backends without dynamic KV-length correction.
Test Coverage
PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.